package kvraft

import (
	"../labrpc"
	"time"
)
import "crypto/rand"
import "math/big"

type Clerk struct {
	servers []*labrpc.ClientEnd
	// You will have to modify this struct.
	leader int
	me     int64
	rid    int32
}

func nrand() int64 {
	max := big.NewInt(int64(1) << 62)
	bigx, _ := rand.Int(rand.Reader, max)
	x := bigx.Int64()
	return x
}

func MakeClerk(servers []*labrpc.ClientEnd) *Clerk {
	ck := new(Clerk)
	ck.servers = servers
	// You'll have to add code here.
	ck.me = nrand() << 32
	return ck
}

//
// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.Get", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
//
func (ck *Clerk) Get(key string) string {
	// You will have to modify this function.

	args := GetArgs{Key: key}

	retry := make(chan bool)
	done := make(chan bool)
	for {
		timeout := time.After(3 * time.Second)
		reply := GetReply{}
		go func() {
			ok := ck.servers[ck.leader].Call("KVServer.Get", &args, &reply)
			if !ok || reply.Err == ErrWrongLeader {
				retry <- true
			} else {
				done <- true
			}
		}()

		select {
		case <-done:
			DPrintf("GetResponse->reply:%+v,args:%+v", reply, args)
			return reply.Value
		case <-retry:
			if reply.Err == ErrWrongLeader {
				DPrintf("GetResponse->%d is not leader,reply:%+v,args:%+v", ck.leader, reply, args)
				ck.leader = (ck.leader + 1) % len(ck.servers)
				time.Sleep(50 * time.Millisecond)
			} else {
				DPrintf("GetResponse->send RPC failed,reply:%+v,args:%+v", reply, args)
			}
		case <-timeout:
			DPrintf("GetResponse->timeout,reply:%+v,args:%+v", reply, args)
		}
	}
}

//
// shared by Put and Append.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.PutAppend", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
//
func (ck *Clerk) PutAppend(key string, value string, op string) {
	// You will have to modify this function.
	ck.rid += 1
	args := PutAppendArgs{
		Key:   key,
		Value: value,
		Op:    op,
		Id:    ck.me | int64(ck.rid),
	}

	retry := make(chan bool)
	done := make(chan bool)
	for {
		timeout := time.After(3 * time.Second)
		reply := PutAppendReply{}
		go func() {
			ok := ck.servers[ck.leader].Call("KVServer.PutAppend", &args, &reply)
			if !ok || reply.Err == ErrWrongLeader {
				retry <- true
			} else {
				done <- true
			}
		}()

		select {
		case <-done:
			DPrintf("PutAppendResponse->reply:%+v,args:%+v", reply, args)
			return
		case <-retry:
			if reply.Err == ErrWrongLeader {
				DPrintf("PutAppendResponse->%d is not leader,reply:%+v,args:%+v", ck.leader, reply, args)
				ck.leader = (ck.leader + 1) % len(ck.servers)
				time.Sleep(50 * time.Millisecond)
			} else {
				DPrintf("PutAppendResponse->send RPC failed,reply:%+v,args:%+v", reply, args)
			}
		case <-timeout:
			DPrintf("PutAppendResponse->timeout,reply:%+v,args:%+v", reply, args)
		}
	}
}

func (ck *Clerk) Put(key string, value string) {
	ck.PutAppend(key, value, PUT)
}
func (ck *Clerk) Append(key string, value string) {
	ck.PutAppend(key, value, APPEND)
}
